#include "overview.h"

#include "OldGraphicsView/Scene.h"

#include <QMouseEvent>
#include <QDebug>

namespace FootprintEditor
{

    OverView::OverView(QWidget *parent) :
        LeGraphicsView::View(parent),
        m_observedView(nullptr),
        m_move(NoMove)
    {
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
        setAlignment(Qt::AlignCenter);
        setInteractive(false);
        setMouseTracking(true);
    }

    void OverView::setObservedView(View *view)
    {
        if (m_observedView)
        {
            m_observedView->viewport()->removeEventFilter(this);
            m_observedView->disconnect(this);
        }

        m_observedView = view;

        if (m_observedView)
        {
            setScene(m_observedView->scene());
            connect(m_observedView, &View::settingsChanged,
                    this, &OverView::synchroniseSettings);
            synchroniseSettings();
        }
    }

    void OverView::updateObservedRect()
    {
        QTransform t = m_observedView->viewportTransform();
        QRectF r = m_observedView->viewport()->rect();
        m_observedRect = t.inverted().mapRect(r);
    }

    void OverView::synchroniseSettings()
    {
        setPalette(m_observedView->palette());
        setHardwareAccelerationEnabled(m_observedView->hardwareAccelerationEnabled());
        setGridEnabled(m_observedView->gridEnabled());
        setMinimalGridSize(m_observedView->minimalGridSize());
        setGridCoarseMultiplier(m_observedView->gridCoarseMultiplier());
        setGridCoarseLineStyle(m_observedView->gridCoarseLineStyle());
        setGridFineLineStyle(m_observedView->gridFineLineStyle());

        setRulerEnabled(false);
        setMouseCursor(LeGraphicsView::View::NoMouseCursor);
        setOriginMark(LeGraphicsView::View::NoOriginMark);
    }

    // All painting is done in scene coordinates. rect is the exposed rectangle
    void OverView::drawForeground(QPainter *painter, const QRectF &rect)
    {
        View::drawForeground(painter, rect);
        painter->setBrush(Qt::NoBrush);
        painter->setPen(QPen(palette().primaryContent(), 3.0 / transform().m11()));
        painter->drawRect(m_observedRect);
    }

    void OverView::resizeEvent(QResizeEvent *event)
    {
        Q_UNUSED(event);
        if (m_observedView && scene())
            fitInView(scene()->sceneRect().adjusted(-50, -50, 50, 50),
                      Qt::KeepAspectRatio);
    }

    void OverView::mousePressEvent(QMouseEvent *event)
    {
        if (event->button() != Qt::LeftButton)
        {
            return;
        }

        QPointF pos = mapToScene(event->pos());
        qreal radius = 5.0 / transform().m11();
        m_lastPos = pos;

        if ((m_observedRect.topLeft() - pos).manhattanLength() < radius)
        {
            m_move = MoveTopLeft;
        }
        else if ((m_observedRect.topRight() - pos).manhattanLength() < radius)
        {
            m_move = MoveTopRight;
        }
        else if ((m_observedRect.bottomLeft() - pos).manhattanLength() < radius)
        {
            m_move = MoveBottomLeft;
        }
        else if ((m_observedRect.bottomRight() - pos).manhattanLength() < radius)
        {
            m_move = MoveBottomRight;
        }
        else if (m_observedRect.contains(pos))
        {
            m_move = MoveRect;
        }
        else
        {
            m_move = NoMove;
        }
    }

    void OverView::mouseMoveEvent(QMouseEvent *event)
    {
        QPointF pos = mapToScene(event->pos());
        qreal radius = 5.0 / transform().m11();
        switch (m_move)
        {
            case NoMove:
                if ((m_observedRect.topLeft() - pos).manhattanLength() < radius)
                {
                    setCursor(Qt::SizeFDiagCursor);
                }
                else if ((m_observedRect.topRight() - pos).manhattanLength() < radius)
                {
                    setCursor(Qt::SizeBDiagCursor);
                }
                else if ((m_observedRect.bottomLeft() - pos).manhattanLength() < radius)
                {
                    setCursor(Qt::SizeBDiagCursor);
                }
                else if ((m_observedRect.bottomRight() - pos).manhattanLength() < radius)
                {
                    setCursor(Qt::SizeFDiagCursor);
                }
                else if (m_observedRect.contains(pos))
                {
                    setCursor(Qt::SizeAllCursor);
                }
                else
                {
                    setCursor(QCursor());
                }
                break;
            case MoveTopLeft:
                m_observedRect.setTopLeft(pos);
                forceRedraw();
                break;
            case MoveTopRight:
                m_observedRect.setTopRight(pos);
                forceRedraw();
                break;
            case MoveBottomLeft:
                m_observedRect.setBottomLeft(pos);
                forceRedraw();
                break;
            case MoveBottomRight:
                m_observedRect.setBottomRight(pos);
                forceRedraw();
                break;
            case MoveRect:
                QPointF delta = pos - m_lastPos;
                m_observedView->centerOn(m_observedRect.translated(delta).center());
                break;
        }
        m_lastPos = pos;
    }

    void OverView::mouseReleaseEvent(QMouseEvent *event)
    {
        Q_UNUSED(event);

        switch (m_move)
        {
            case MoveTopLeft:
            case MoveTopRight:
            case MoveBottomLeft:
            case MoveBottomRight:
                m_observedView->resetMatrix();
                m_observedView->resetTransform();
                m_observedView->fitInView(m_observedRect, Qt::KeepAspectRatio);
                break;
            case NoMove:
            case MoveRect:
                break;
        }
        m_move = NoMove;
        setCursor(QCursor());
    }

    bool OverView::eventFilter(QObject *obj, QEvent *ev)
    {
        // TODO: not efficient, get something better?
        if (m_observedView && obj == m_observedView->viewport() && ev->type() == QEvent::Paint)
        {
            updateObservedRect();
            forceRedraw();
        }
        return View::eventFilter(obj, ev);
    }

    void OverView::forceRedraw()
    {
        // TODO: get something better?
        viewport()->update();
    }


    void OverView::showEvent(QShowEvent *event)
    {
        Q_UNUSED(event);
        if (m_observedView != nullptr)
        {
            m_observedView->viewport()->installEventFilter(this);
        }
    }

}
